<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang="en">
<head>
	<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
	<title>进阶：18、自定义指令</title>
	<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
	<div id="hook-arguments-example" v-demo:hello.a.b="message"></div>

	<script type="text/javascript">
		// -------------------------------- 简介 --------------------------------
		// 在基础篇，介绍了vue的各种v-前缀的指令，这些指令都是框架封装好的，如果需要自定义指令
		// 可以使用vue的directives选项：
		// （1）全局注册
		// // 注册一个全局自定义指令 v-focus
		// Vue.directive('focus', {
		//   // 当绑定元素插入到 DOM 中。
		//   inserted: function (el) {
		//     // 聚焦元素
		//     el.focus()
		//   }
		// })
		// 
		// （2）局部(组件内)注册
		// directives: {
		//   focus: {
		//     // 指令的定义---
		//   }
		// }
		// 
		// 上面两种注册方式都注册了一个v-focus的指令，可以在标签里像系统自带的v-前缀属性一样使用
		

		// ------------------------------ 钩子函数 -------------------------------
		// 这个其实有点类似于vue的生命周期钩子，作用域和参数不一样罢了
		// （1）vue的生命周期钩子的作用域是组件，没有参数
		// （2）自定义指令的钩子函数的作用域是使用了自定义指令的元素，带有多个参数
		//       在介绍参数之前，先看看有多少种类型的钩子：
		// a、bind: 只调用一次，指令第一次绑定到元素时调用，用这个钩子函数可以定义一个在绑定时执行一次的初始化动作。
		// b、inserted: 被绑定元素插入父节点时调用（父节点存在即可调用，不必存在于 document 中）。
		// c、update: 被绑定元素所在的模板更新时调用，而不论绑定值是否变化。通过比较更新前后的绑定值，可以忽略不必要的模板更新（详细的钩子函数参数见下）。
		// d、componentUpdated: 被绑定元素所在模板完成一次更新周期时调用。
		// e、unbind: 只调用一次， 指令与元素解绑时调用。
		

		// ---------------------------- 钩子函数参数 ------------------------------
		// 钩子函数的参数有el、binging、vnode、oldVnode，其意义如下：
		// （1）el: 指令所绑定的元素，可以用来直接操作 DOM 。
		// （2）binding: 一个对象，包含以下属性：
		// 			name: 指令名，不包括 v- 前缀。
		// 			value: 指令的绑定值， 例如： v-my-directive="1 + 1", value 的值是 2。
		// 			oldValue: 指令绑定的前一个值，仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。
		// 			expression: 绑定值的字符串形式。 例如 v-my-directive="1 + 1" ， expression 的值是 "1 + 1"。
		// 			arg: 传给指令的参数。例如 v-my-directive:foo， arg 的值是 "foo"。
		// 			modifiers: 一个包含修饰符的对象。 例如： v-my-directive.foo.bar, 修饰符对象 modifiers 的值是 { foo: true, bar: true }。
		// （3）vnode: Vue 编译生成的虚拟节点，查阅 VNode API 了解更多详情。
		// （4）oldVnode: 上一个虚拟节点，仅在 update 和 componentUpdated 钩子中可用。
		

		// 文档实例demo
		// 定义一个自定义指令
		// 在一般情况下，使用el和binding这两个参数比较多，暂时不要去管另外两个参数
		Vue.directive('demo',{
			bind: function(el, binding, vnode){
				// JSON.stringify就是将js值转换为JSON格式字符串
				// 这里
				// # var s= JSON.stringify
				// # s(value)
				// 的语法等同于
				// # JSON.stringify(value)
				var s = JSON.stringify
				// binding是一个对象，包含了el指向的元素的很多属性的集合 
				// 定义el指定元素的innerHTML的值，会在元素内部显示binding的属性
				// 在浏览器上打开这个页面，显示的内容如下：
				// name: "demo"
				// value: "hello!"
				// expression: "message"
				// argument: "hello"
				// modifiers: {"a":true,"b":true}
				// vnode keys: tag, data, children, text, elm, ns, context, functionalContext, key, componentOptions, componentInstance, parent, raw, isStatic, isRootInsert, isComment, isCloned, isOnce
			    el.innerHTML =
			       // binding的name属性就是自定义的指令v-xxx后面的xxx,这里是v-demo后面的demo
			      'name: '       + s(binding.name) + '<br>' +
			      // binding的value属性就是el元素绑定的vue实例中的数据，这里绑定的是message，
			      // 对应实例中的message，所以是hello!
			      'value: '      + s(binding.value) + '<br>' +
			      // express就是绑定的表达式的字符串形式，跟上面的value是联系在一起的
			      'expression: ' + s(binding.expression) + '<br>' +
			      // arg是传给指令的参数，也就是v-xxx:yyy.a.b的yyy部分
			      'argument: '   + s(binding.arg) + '<br>' +
			      // modifiers是修饰符对象，也就是v-xxx:yyy.a.b的.a.b部分
			      // 对象为{ a:true, b:true }
			      'modifiers: '  + s(binding.modifiers) + '<br>' +
			      'vnode keys: ' + Object.keys(vnode).join(', ')		

			      // 看起来binding的各个属性很陌生，也不知道有什么应用场景，
			      // 但你参考一下vue封装的东西，相信你一下子能理解了！
			      // eg:
			      // value和expression这两个不言自明，就是实际绑定的值和表达式嘛！
			      // argument，就是绑定的参数名
			      // 参考一下我们熟悉的v-bind的使用：
			      // 	v-bind:attr="expression"
			      // 	...,
			      // 	data: {
			      // 	   expression: 'hello vue'
			      // 	}
			      // 	...
			      // 	
			      // 	attr就是上面说的argument
			      // 	express就是vue实例的的数据或计算属性或者单目js表达式
			      // 	'hello vue'就是express的值即value
			      //  	
			      // 是不是一下子明白了！O(∩_∩)O~
			      // 
			      // modifiers是修饰符，同样回想一下vue中有哪些自带的修饰符？
			      // (1).once是不是，这个修饰符的作用是初始化渲染而不绑定数据
			      // 常见的使用形式是v-bind:attr.once=""
			      // (2)在数据格式声明上的修饰符
			      // 有.number，声明绑定数据是数字
			      // 常见的使用形式有v-model.number="age"
			      // 还有.lazy、.trim
			      // (3)对于v-on，在对自定义组件绑定原生事件时修改添加.native修饰符
			      // <my-component v-on:click.native=""></my-component>
			      // 
			      // 那我们可以怎么利用修饰符呢？
			      // 在钩子函数中，通过判断是否有某个修饰符来判断是否要对其进行处理
			      // eg:
			      // ...
			      // bind: function(el, binding){
			      // 	 var modifiers = binding.modifiers
			      // 	 // 如果使用了.a修饰符
			      // 	 if(modifiers.a){
			      // 	 	 // 执行.a操作符应有的操作
			      // 	 }
			      // 	 
			      // 	 // 如果使用了.b修饰符
			      // 	 if(modifiers.b){
			      // 	 	 // 执行.a操作符应有的操作
			      // 	 }
			      // 	 
			      // 	 ... 以此类推 ...
			      // }
			}
		})
		// vue实例
		new Vue({
		  el: '#hook-arguments-example',
		  data: {
		    message: 'hello!'		    
		  }
		})


		// ------------------------------- 函数简写 -------------------------------
		// 大多数情况下，我们可能想在 bind 和 update 钩子上做重复动作，并且不想关心其它的钩子函数。可以这样写:
		// Vue.directive('color-swatch', function (el, binding) {
		//   el.style.backgroundColor = binding.value
		// })
		

		// 对象字面量
		// 如果指令需要多个值，可以传入一个 JavaScript 对象字面量。记住，指令函数能够接受所有合法类型的 JavaScript 表达式。
		// <div v-demo="{ color: 'white', text: 'hello!' }"></div>
		// Vue.directive('demo', function (el, binding) {
		//   console.log(binding.value.color) // => "white"
		//   console.log(binding.value.text)  // => "hello!"
		// })
	</script>
</body>
</html>